﻿#include "WidgetPLCServer.h"
#include <QFormLayout>
#include <QSerialPortInfo>
#include <QInputDialog>
#include <QMessageBox>
#include <QLabel>

#pragma execution_character_set("utf-8")

WidgetPLCServer::WidgetPLCServer(const QString& title, QWidget *parent)
    : QGroupBox{title, parent}
{
    setupUI();
    initConnection();
    mMapPLCServer.clear();
}

void WidgetPLCServer::addPLCServer(PLCServerBase *server)
{
    if(mMapPLCServer.contains(server->mConfig.name))
        return;
    mMapPLCServer.insert(server->mConfig.name, server);
    cbboxComNameList->addItem(server->mConfig.name);
    cbboxComNameList->setCurrentText(server->mConfig.name);
    connect(server, &PLCServerBase::sigStateChanged, this, [&](const ComState& state){
        if(cbboxComNameList->currentText() == mMapPLCServer.value(cbboxComNameList->currentText())->mConfig.name){
            txtedtDataRev->append(PLCServerBase::mapPLCServerStateText.value(state));
        }
    });
    connect(server, &PLCServerBase::sigData, this, [&](const PLCRequestData& data){
        if(cbboxComNameList->currentText() == mMapPLCServer.value(cbboxComNameList->currentText())->mConfig.name){
            if(mIsRequest){
                for(int i = 0; i< data.data.size(); ++i){
                    QString varTxt;
                    switch(data.data.at(i).valueType){
                    case VT_Bool:
                    case VT_Int16:
                        varTxt = QString::number(data.data.at(i).valueI);
                        break;
                    case VT_Int32:
                        varTxt = QString::number(data.data.at(i).valueDI);
                        break;
                    case VT_Float:
                        varTxt = QString::number(data.data.at(i).valueF, 'f', 3);
                        break;
                    }
                    tblwgtReadList->item(i,1)->setText(varTxt);
                }
                mIsRequest = false;
            } else {
                if(chkboxIsDisplayData->isChecked()){
                    QString strDisplay;
                    for(auto value : data.data){
                        strDisplay += QString("%1,%2;").arg(data.addrStart + value.addr)
                                .arg(value.valueType == VT_Float ? value.valueF : value.valueI);
                    }
                    strDisplay.replace(strDisplay.length() - 1, 1, "\n");
                    txtedtDataRev->append(strDisplay);
                }
            }
        }
    });

    connect(server, &PLCServerBase::sigErrOccur, this, [&](const QString& str){
        if(cbboxComNameList->currentText() == mMapPLCServer.value(cbboxComNameList->currentText())->mConfig.name){
            txtedtDataRev->append(str);
        }
    });
    connect(this, &WidgetPLCServer::sigConfigChanged, server, &PLCServerBase::onPLCConfigChanged);

}
void WidgetPLCServer::setupUI()
{
    mLayout = new QHBoxLayout();
    this->setLayout(mLayout);
    setupConfigUI();
    setupSendRev();
}

void WidgetPLCServer::setupConfigUI()
{
    pbtnAddOne = new QPushButton("添加");
    pbtnDeleteOne = new QPushButton("删除");
    pbtnApplyPLC = new QPushButton("应用");
    cbboxComNameList = new QComboBox();
    cbboxComTypeList = new QComboBox();
    spboxServerID = new QSpinBox();

    cbboxComTypeList->addItems(PLCServerBase::mapPLCServerTypeText.values());
    spboxServerID->setRange(1,255);

    QFormLayout* fLayout = new QFormLayout();
    mLayout->addLayout(fLayout);

    QHBoxLayout* hLayout = new QHBoxLayout();
    fLayout->addRow(hLayout);
    hLayout->addWidget(pbtnAddOne);
    hLayout->addWidget(pbtnDeleteOne);
    hLayout->addWidget(pbtnApplyPLC);
    fLayout->addRow("通讯列表", cbboxComNameList);
    fLayout->addRow("协议类型", cbboxComTypeList);
    fLayout->addRow("服务ID", spboxServerID);

    {
        //串口区域
        wgtSerialConfig = new QWidget();
        fLayout->addRow(wgtSerialConfig);
        QFormLayout* fSerialLayout = new QFormLayout();
        wgtSerialConfig->setLayout(fSerialLayout);
        cbboxSerialList = new QComboBox();
        cbboxSerialBaudRate = new QComboBox();
        cbboxSerialDataBits = new QComboBox();
        cbboxSerialParity = new QComboBox();
        cbboxSerialStopBits = new QComboBox();
        cbboxSerialFlowControl = new QComboBox();

        const auto infos = QSerialPortInfo::availablePorts();
        QStringList list;
        for (const QSerialPortInfo &info : infos) {
            list << info.portName();
        }
        cbboxSerialList->addItems(list);

        cbboxSerialBaudRate->addItem(QStringLiteral("9600"), QSerialPort::Baud9600);
        cbboxSerialBaudRate->addItem(QStringLiteral("19200"), QSerialPort::Baud19200);
        cbboxSerialBaudRate->addItem(QStringLiteral("38400"), QSerialPort::Baud38400);
        cbboxSerialBaudRate->addItem(QStringLiteral("115200"), QSerialPort::Baud115200);

        cbboxSerialDataBits->addItem(QStringLiteral("5"), QSerialPort::Data5);
        cbboxSerialDataBits->addItem(QStringLiteral("6"), QSerialPort::Data6);
        cbboxSerialDataBits->addItem(QStringLiteral("7"), QSerialPort::Data7);
        cbboxSerialDataBits->addItem(QStringLiteral("8"), QSerialPort::Data8);
        cbboxSerialDataBits->setCurrentIndex(3);

        cbboxSerialParity->addItem(tr("None"), QSerialPort::NoParity);
        cbboxSerialParity->addItem(tr("Even"), QSerialPort::EvenParity);
        cbboxSerialParity->addItem(tr("Odd"), QSerialPort::OddParity);
        cbboxSerialParity->addItem(tr("Mark"), QSerialPort::MarkParity);
        cbboxSerialParity->addItem(tr("Space"), QSerialPort::SpaceParity);

        cbboxSerialStopBits->addItem(QStringLiteral("1"), QSerialPort::OneStop);
#ifdef Q_OS_WIN
        cbboxSerialStopBits->addItem(tr("1.5"), QSerialPort::OneAndHalfStop);
#endif
        cbboxSerialStopBits->addItem(QStringLiteral("2"), QSerialPort::TwoStop);

        cbboxSerialFlowControl->addItem(tr("None"), QSerialPort::NoFlowControl);
        cbboxSerialFlowControl->addItem(tr("RTS/CTS"), QSerialPort::HardwareControl);
        cbboxSerialFlowControl->addItem(tr("XON/XOFF"), QSerialPort::SoftwareControl);

        fSerialLayout->addRow("串口号", cbboxSerialList);
        fSerialLayout->addRow("波特率", cbboxSerialBaudRate);
        fSerialLayout->addRow("数据位", cbboxSerialDataBits);
        fSerialLayout->addRow("校验", cbboxSerialParity);
        fSerialLayout->addRow("停止位", cbboxSerialStopBits);
        fSerialLayout->addRow("流控制", cbboxSerialFlowControl);
        wgtSerialConfig->setVisible(false);
    }
    {
        //网口区域
        wgtSocketConfig = new QWidget();
        fLayout->addRow(wgtSocketConfig);
        QFormLayout* fSocketLayout = new QFormLayout();
        wgtSocketConfig->setLayout(fSocketLayout);
        ledtOpponentIP = new QLineEdit("127.0.0.1");
        spboxOpponentPort = new QSpinBox();
        ledtLocalIP = new QLineEdit("127.0.0.1");
        spboxLocalPort = new QSpinBox();

        spboxOpponentPort->setRange(0,100000);
        spboxLocalPort->setRange(0,100000);
        spboxOpponentPort->setValue(5000);
        spboxLocalPort->setValue(6000);

        fSocketLayout->addRow("本地IP", ledtLocalIP);
        fSocketLayout->addRow("本地端口", spboxLocalPort);
        fSocketLayout->addRow("对方IP", ledtOpponentIP);
        fSocketLayout->addRow("对方端口", spboxOpponentPort);
    }
}
void WidgetPLCServer::setupSendRev()
{
    //接收区
    chkboxIsDisplayData = new QCheckBox("显示数据");
    pbtnClear = new QPushButton("清空");
    txtedtDataRev = new QTextEdit();
    QVBoxLayout* vLayout0 = new QVBoxLayout();
    mLayout->addLayout(vLayout0);
    {
        QHBoxLayout* hLayout1 = new QHBoxLayout();
        hLayout1->addWidget(chkboxIsDisplayData);
        hLayout1->addWidget(pbtnClear);
        vLayout0->addLayout(hLayout1);
    }
    vLayout0->addWidget(txtedtDataRev);
    txtedtDataRev->setStyleSheet("color:black;");

    //读取和写入区
    pbtnDataRead = new QPushButton("读取");
    pbtnDataWrite = new QPushButton("写入");
    pbtnDataAdd = new QPushButton("添加");
    pbtnDataRemove = new QPushButton("移除");
    spboxAddrStart = new QSpinBox();
    cbboxDataType = new QComboBox();
    tblwgtReadList = new QTableWidget();

    cbboxDataType->addItem("线圈", QModbusDataUnit::Coils);
    cbboxDataType->addItem("输入寄存器", QModbusDataUnit::InputRegisters);
    cbboxDataType->addItem("保持寄存器", QModbusDataUnit::HoldingRegisters);

    tblwgtReadList->setEditTriggers(QAbstractItemView::NoEditTriggers);
    tblwgtReadList->setColumnCount(2);
    tblwgtReadList->setVerticalHeaderLabels({"数据类型","值"});
    QHBoxLayout* hLayoutSend = new QHBoxLayout();
    QVBoxLayout* vLayoutSend = new QVBoxLayout();
    hLayoutSend->addWidget(tblwgtReadList);
    hLayoutSend->addLayout(vLayoutSend);
    vLayoutSend->addWidget(new QLabel("起始地址"));
    vLayoutSend->addWidget(spboxAddrStart);
    vLayoutSend->addWidget(new QLabel("寄存器类型"));
    vLayoutSend->addWidget(cbboxDataType);
    vLayoutSend->addWidget(new QLabel(""));
    vLayoutSend->addWidget(pbtnDataAdd);
    vLayoutSend->addWidget(pbtnDataRemove);
    vLayoutSend->addWidget(pbtnDataRead);
    vLayoutSend->addWidget(pbtnDataWrite);
    vLayoutSend->addStretch();
    vLayout0->addLayout(hLayoutSend);
}

void WidgetPLCServer::initConnection()
{
    connect(pbtnAddOne, &QAbstractButton::clicked, this, [&](){
        QString type = QInputDialog::getItem(this, "选择通讯协议", "通讯协议", PLCServerBase::mapPLCServerTypeText.values(), 0, false);
        QString name = QInputDialog::getText(nullptr,"输入新增通信名称","名称");
        if(name.isEmpty() || type.isEmpty())
            return;
        PLCServerConfig config = getConfig();
        config.name = name;
        config.type = PLCServerType(PLCServerBase::mapPLCServerTypeText.values().indexOf(type));
        emit sigAddOne(config);
    });
    connect(pbtnDeleteOne, &QAbstractButton::clicked, this, [&](){
        if(!(cbboxComNameList->count() > 0))
            return;
        QMessageBox msgBox(QMessageBox::Question, "提示","确认删除此通信吗?",
                           QMessageBox::Yes | QMessageBox::No);
        msgBox.button(QMessageBox::Yes)->setText("确认");
        msgBox.button(QMessageBox::No)->setText("取消");
        if(QMessageBox::No == msgBox.exec())
            return;
        mMapPLCServer.remove(cbboxComNameList->currentText());
        emit sigDeleteOne(cbboxComNameList->currentText());
        cbboxComNameList->removeItem(cbboxComNameList->currentIndex());
    });
    connect(pbtnApplyPLC, &QAbstractButton::clicked, this, &WidgetPLCServer::onSaveConfig);
    connect(cbboxComNameList, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &WidgetPLCServer::onComSelectChanged);
    connect(cbboxComTypeList, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [&](const int& index){
        PLCServerType type = PLCServerType(index);
        wgtSocketConfig->setVisible(type == PST_ModbusTcp);
        wgtSerialConfig->setVisible(type == PST_ModbusRtu);
    });

    connect(pbtnClear, &QAbstractButton::clicked, this, [&](){
        txtedtDataRev->clear();
    });

    connect(pbtnDataAdd, &QAbstractButton::clicked, this, [&](){
        tblwgtReadList->insertRow(tblwgtReadList->rowCount());
    });
    connect(pbtnDataRemove, &QAbstractButton::clicked, this, [&](){
        tblwgtReadList->removeRow(tblwgtReadList->currentRow());
    });
    connect(tblwgtReadList, &QTableWidget::cellDoubleClicked, this, [&](int row, int col){
        QTableWidgetItem* item = tblwgtReadList->item(row, col);
        if(!item){
            item = new QTableWidgetItem();
            tblwgtReadList->setItem(row, col, item);
        }
        if(col == 0){
            int index = valueTypeString.values().indexOf(item->text());
            QString txt = getTxt(index);
            item->setText(txt);
        } else if(col == 1){
            float value = item->text().toDouble();
            double curValue = getValue(value);
            QString typeStr = tblwgtReadList->item(item->row(),0)->text();
            ValueType varType = static_cast<ValueType>(valueTypeString.values().indexOf(typeStr));
            QString txt = varType == VT_Float ? QString::number(curValue, 'f', 3) : QString::number(curValue);
            item->setText(txt);
        }
    });
    connect(pbtnDataRead, &QAbstractButton::clicked, this, [&](){
        PLCRequestData sendData;
        sendData.table = cbboxDataType->currentData().value<QModbusDataUnit::RegisterType>();
        sendData.addrStart = spboxAddrStart->value();
        QList<ModbusValue> valueList;
        int index = 0;
        for(int i = 0; i < tblwgtReadList->rowCount(); ++i){
            QString typeStr = tblwgtReadList->item(i,0)->text();
            ValueType varType = static_cast<ValueType>(valueTypeString.values().indexOf(typeStr));
            ModbusValue value;
            value.addr = index;
            value.valueType = varType;
            if(varType == VT_Int16 || varType == VT_Bool){
                value.valueI =  tblwgtReadList->item(i,1)->text().toInt();
                index += 1;
            } else if(varType == VT_Int32){
                value.valueDI =  tblwgtReadList->item(i,1)->text().toInt();
                index += 2;
            } else if(varType == VT_Float){
                value.valueF =  tblwgtReadList->item(i,1)->text().toFloat();
                index += 2;
            }
            valueList << value;
        }
        sendData.data = valueList;
        mIsRequest = true;
        mMapPLCServer.value(cbboxComNameList->currentText())->onRead(sendData);
    });
    connect(pbtnDataWrite, &QAbstractButton::clicked, this, [&](){
        PLCRequestData sendData;
        sendData.table = cbboxDataType->currentData().value<QModbusDataUnit::RegisterType>();
        sendData.addrStart = spboxAddrStart->value();
        QList<ModbusValue> valueList;
        int index = 0;
        for(int i = 0; i < tblwgtReadList->rowCount(); ++i){
            QString typeStr = tblwgtReadList->item(i,0)->text();
            ValueType varType = static_cast<ValueType>(valueTypeString.values().indexOf(typeStr));
            ModbusValue value;
            value.addr = index;
            value.valueType = varType;
            if(varType == VT_Int16 || varType == VT_Bool){
                value.valueI =  tblwgtReadList->item(i,1)->text().toInt();
                index += 1;
            } else if(varType == VT_Int32){
                value.valueDI =  tblwgtReadList->item(i,1)->text().toInt();
                index += 2;
            } else if(varType == VT_Float){
                value.valueF =  tblwgtReadList->item(i,1)->text().toFloat();
                index += 2;
            }
            valueList << value;
        }
        sendData.data = valueList;
        mIsRequest = true;
        mMapPLCServer.value(cbboxComNameList->currentText())->onRead(sendData);
    });
}

PLCServerConfig WidgetPLCServer::getConfig()
{
    PLCServerConfig config;
    config.name = cbboxComNameList->currentText();
    config.serverID = spboxServerID->value();
    switch (config.type)
    {
    case PST_ModbusTcp:
    {
        TcpComConfig tcpCfg;
        tcpCfg.name = config.name;
        tcpCfg.mOpponentIP = ledtOpponentIP->text();
        tcpCfg.mOpponentPort = spboxOpponentPort->value();
        tcpCfg.mLocalIP = ledtLocalIP->text();
        tcpCfg.mLocalPort = spboxLocalPort->value();
        config.tcp = tcpCfg;
        config.type = PST_ModbusTcp;
    }
        break;
    case PST_ModbusRtu:
    {
        SerialComConfig serialCfg;
        serialCfg.name = config.name;
        serialCfg.comId = cbboxSerialList->currentText();
        serialCfg.baudRate = cbboxSerialBaudRate->currentData().toInt();
        serialCfg.dataBits = static_cast<QSerialPort::DataBits>(cbboxSerialDataBits->currentData().toInt());
        serialCfg.parity = static_cast<QSerialPort::Parity>(cbboxSerialParity->currentText().toInt());
        serialCfg.stopBits = static_cast<QSerialPort::StopBits>(cbboxSerialStopBits->currentText().toInt());
        serialCfg.flowControl = static_cast<QSerialPort::FlowControl>(cbboxSerialFlowControl->currentText().toInt());
        config.serial = serialCfg;
        config.type = PST_ModbusRtu;
    }
        break;
    default:
        break;
    }
    return config;
}

void WidgetPLCServer::onSaveConfig()
{
    PLCServerConfig config = getConfig();
    emit sigConfigChanged(config);
}

void WidgetPLCServer::onComSelectChanged(const int& index)
{
    if(index < 0)
        return;
    PLCServerConfig config = mMapPLCServer.value(cbboxComNameList->currentText())->mConfig;
    cbboxComTypeList->setCurrentIndex(config.type);
    spboxServerID->setValue(config.serverID);
    PLCServerType type = config.type;
    if(type == PST_ModbusTcp){
        ledtOpponentIP->setText(config.tcp.mOpponentIP);
        spboxOpponentPort->setValue(config.tcp.mOpponentPort);
        ledtLocalIP->setText(config.tcp.mLocalIP);
        spboxLocalPort->setValue(config.tcp.mLocalPort);
    }else if(type == PST_ModbusRtu){
        cbboxSerialList->setCurrentIndex(cbboxSerialList->findText(config.serial.name));
        cbboxSerialBaudRate->setCurrentIndex(cbboxSerialBaudRate->findData(config.serial.baudRate));
        cbboxSerialDataBits->setCurrentIndex(cbboxSerialDataBits->findData(config.serial.dataBits));
        cbboxSerialParity->setCurrentIndex(cbboxSerialParity->findData(config.serial.parity));
        cbboxSerialStopBits->setCurrentIndex(cbboxSerialStopBits->findData(config.serial.stopBits));
        cbboxSerialFlowControl->setCurrentIndex(cbboxSerialFlowControl->findData(config.serial.flowControl));
    }
}
QString WidgetPLCServer::getTxt(int index)
{
    return QInputDialog::getItem(this, "选择数据类型", "类型", valueTypeString.values(), index, false);;
}
double WidgetPLCServer::getValue(int value)
{
    double val = QInputDialog::getDouble(this, "输入值", "值", value, false);
    return val;
}
